#ifndef __comm_HPP__
#define __comm_HPP__

#include <iostream>
#include <string>
#include <cstring>
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/shm.h>
#include <unistd.h>

using namespace std;

const char *Path = "/home/xdt/study_-linux/shm_ipc"; // 创建key需要的参数
const int proj_id = 0x77;							 // 创建key要的参数

const int defaultsize = 4096; // 共享内存空间的默认大小，但是linux中是会以4KB为基本单位的

// 转16进制
string ToHex(key_t k)
{
	char buffer[1024];
	snprintf(buffer, sizeof(buffer), "0x%x", k);
	return buffer;
}

// 获取共享内存空间的唯一值
key_t GetShmKeyOrDie()
{
	key_t key = ftok(Path, proj_id);
	if (key < 0)
	{
		cerr << "ftok error,errno: " << errno << ", error massage: " << strerror(errno) << endl;
		exit(1);
	}

	return key;
}

// 创建内存共享空间，返回共享空间标识符
int CreatShmOrDie(key_t key, size_t size, int shmflg)
{
	int shmid = shmget(key, size, shmflg);
	if (shmid < 0)
	{
		cerr << "shmget error,errno: " << errno << ", error massage: " << strerror(errno) << endl;
		exit(2);
	}

	return shmid;
}

// 创建共享
//  shmget的shmflg
//  IPC_CREAT：不存在就创建，存在就获取
//  IPC_EXCL：没有意义
//  IPC_CREAT | IPC_EXCL 不存在就创建，存在就返回
int CreatShm(key_t key, size_t size)
{
	int shmid = CreatShmOrDie(key, size, IPC_CREAT | IPC_EXCL | 0666); // 后三位留了位置用来设置权限的
	return shmid;
}

// 获取共享
int GetShm(key_t key, size_t size)
{
	int shmid = CreatShmOrDie(key, size, IPC_CREAT);
	return shmid;
}

// 共享空间回收
void DeleteShm(int shmid)
{
	int n = shmctl(shmid, IPC_RMID, nullptr);
	if (n < 0)
	{
		cout << "DeleteShm error" << endl;
	}
	else
	{
		cout << "shmctl delete shm success..." << endl;
	}
}

// 测试
void ShmDubge(int shmid)
{
	struct shmid_ds shmds;
	int n = shmctl(shmid, IPC_STAT, &shmds);
	// if (n < 0)
	// {
	// 	cout << "shmctl error" << endl;
	// 	return;
	// }

	std::cout << "shmds.shm_segsz: " << shmds.shm_segsz << std::endl;
	std::cout << "shmds.shm_nattch:" << shmds.shm_nattch << std::endl;
	std::cout << "shmds.shm_ctime:" << shmds.shm_ctime << std::endl;
	std::cout << "shmds.shm_perm.__key:" << ToHex(shmds.shm_perm.__key) << std::endl;
}

// 挂载
void *ShmAttach(int shmid)
{
	void *addr = shmat(shmid, nullptr, 0);
	if ((long long int)addr == -1)
	{
		cout << "shmat error" << endl;
		return nullptr;
	}

	return addr;
}

// 取消挂载
void ShmDetach(void *addr)
{
	int n = shmdt(addr);
	if (n < 0)
	{
		cout << "shmdt error" << endl;
	}
}
#endif